package com.gm.wj.util;

import com.gm.wj.entity.SysLog;
import com.gm.wj.entity.User;
import com.gm.wj.service.SysLogService;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.management.remote.rmi._RMIConnection_Stub;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @program: wj
 * @description: 日志切面
 * @author: Huang_shengjun
 * @create: 2020-08-22 00:04
 **/
@Component
@Aspect
@Slf4j
public class LogAspect {

    // 松绑以下，进行操作日志记录
    @Autowired
    private SysLogService sysLogService;

    /**
     * @Description: 切入点定义，控制层下面的所有方法的所有值,排除自身log的方法
     * @Param: []
     * @return: void
     * @Author: Huang_shengjun
     * @Date: 2020/4/1 23:56
     */
    @Pointcut("execution(* com.gm.wj.controller.*.*(..)) && !execution(* com.gm.wj.controller.PayController.*(..))")
//    @Pointcut("execution(* com.gm.wj.controller.UploadController.*(..)) && !execution(* com.gm.wj.controller.PayController.*(..))")
    public void logPointCut() {
    }

    /**
     * @Description: 前置通知，在目标方法执行之前执行
     * 通过 JoinPoint参数可以获取目标方法的方法名、修饰符等信息
     * @Param: [jp]
     * @return: void
     * @Author: Huang_shengjun
     * @Date: 2020/4/1 23:56
     */
    @Before(value = "logPointCut()")
    public void before(JoinPoint jp) {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        String name = jp.getSignature().getName();
        log.info(" -- -- ❤ { " + name + " } 开始 ❤ ❤ -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  " );
        log.info(" -- >  ❤ { " + name + " } 方法开始执行");
        // 打印请求url
        log.info(" -- >  ❤ 请求URL           ：" + request.getRequestURI().toString() );
        // 打印Http请求方式
        log.info(" -- >  ❤ HTTP Method      ：" + request.getRequestURI().toString() );
        // 打印请求的入参
        log.info(" -- >  ❤ 请求的入参         ：" + new Gson().toJson(jp.getArgs()) );
        // 打印调用 controller的全路径以及执行方法
        // log.info(" -- > controller的全路径 ：" + jp.getSignature().getDeclaringTypeName() );
        // log.info(" -- > controller的全路径 ：" + jp.getSignature().getName() );

    }


    /**
     * @Description:后置通知，在目标方法执行之后执行
     * @Param: [jp]
     * @return: void
     * @Author: Huang_shengjun
     * @Date: 2020/4/1 23:55
     */
    @After(value = "logPointCut()")
    public void after(JoinPoint jp){
        String name = jp.getSignature().getName();
        log.info(" -- >  ❤ { " + name + " } 方法执行结束");
    }

    /**
     * @Description: 返回通知，获取目标方法的返回值 ，returning 参数是指返回值的变量名，对应方法的参数
     * @Param: [jp, result]
     * @return: void
     * @Author: Huang_shengjun
     * @Date: 2020/4/1 23:59
     */
    @AfterReturning(value = "logPointCut()", returning ="result")
    public void afterReturning(JoinPoint jp ,Object result){
        String name = jp.getSignature().getName();
        //输出
        String list = GsonUtil.GsonString(result);
        log.info(" -- >  ❤ { " + name + " } 方法返回值为：==Gson== " + list);
        log.info(" -- -- -- { " + name + " } 结束 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  " );
    }


    /**
     * @Description: 异常通知
     * @Param: [jp, e]
     * @return: void
     * @Author: Huang_shengjun
     * @Date: 2020/4/2 0:01
     */
    @AfterThrowing(value = "logPointCut()", throwing ="e")
    public void afterThrowing(JoinPoint jp ,Exception e){
        String name = jp.getSignature().getName();
        e.printStackTrace();
        log.info(" -- >  ❌ { " + name + " } 方法抛异常了，异常是：" + e.getMessage());
        log.info(" -- -- ❌ ❌ { " + name + " } 异常结束 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --  " );
    }

    /**
     * @Description: 环绕通知 是所有通知
     * 里功能最为强大的通知，可以实现前直通知、后直通知、异常通知以及返回通知的功能
     * @Param: [pjp]
     * @return: java.lang.Object
     * @Author: Huang_shengjun
     * @Date: 2020/4/2 0:01
     */
    @Around("logPointCut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        long beginTime = System.currentTimeMillis();
        Object result = pjp.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(pjp, time);
        } catch (Exception e) {
        }
        return result;
    }


    private void saveLog(ProceedingJoinPoint joinPoint, long time) {
        // 获取操作用户名
        String username = (String) SecurityUtils.getSubject().getPrincipal();
        if (username == null || username.equals("") ) {
            username = "【未登录用户】";
        }
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        SysLog sysLog = new SysLog();

        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        //读取session中的用户 以及IP地址
        User user = (User) session.getAttribute("user");
        String ip = IpUtil.getIpAddr(request);
        sysLog.setIp(ip);

        SysLoging sysLoging = method.getAnnotation(SysLoging.class);
        if (sysLoging != null) {
            //控制层注解上的描述
            sysLog.setRemark(sysLoging.value());
        }
        // 请求的类名、方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLog.setClassesName(className);
        sysLog.setMethodsName(methodName);
        sysLog.setUserName(username);
        sysLog.setCreateTime(MyTime.getStringDate());
        sysLog.setRunTime(time);
        // 请求的参数
        Object[] args = joinPoint.getArgs();
        try {
            List<String> list = new ArrayList<String>();
            for (Object o : args) {
                list.add(new Gson().toJson(o));
            }
            sysLog.setParamses(list.toString());
        } catch (Exception e) {
            log.info(e.getMessage());
        }
        log.info(" -- >  ❤ 操作日志 > " + GsonUtil.GsonString(sysLog));
        sysLogService.addOrUpdate(sysLog);
    }

}
